import time,sys
from jili.core.printlog import print
from xtquant.xttrader import XtQuantTrader, XtQuantTraderCallback
from xtquant.xttype import StockAccount
from xtquant import xtconstant
import datetime
from jili.xtlib.xtdict import code2name_order_type,code2name_order_status,code2name_price_type
from jili.ibot import notic_qywx
from copy import deepcopy
from jili.xtlib import tool
from jili.price.price_xt import pricetool
class onTraderTool(XtQuantTraderCallback):
    def __init__(self,tradertool,account,ss,ssconfig):
        self.tradertool=tradertool
        self.orders_watchdog={}
        self.account=account
        self.orders_watchdog_closeall = {}
        self.ss=ss
        self.working_orders={}
        self.isibot=False
        self.qywx_bot_key=""
        self.open_objs=[]
        self.reset_order_checktick={}
        self.pricetool0=pricetool()
        if "strategy_name" in ssconfig.keys():
            self.ssname = ssconfig["strategy_name"]
        else:
            self.ssname = ""
        if "qywx_bot_key" in ssconfig.keys():
            self.isibot=True
            self.qywx_bot_key=ssconfig["qywx_bot_key"]
        self.order_end_stat=[xtconstant.ORDER_PART_CANCEL,xtconstant.ORDER_CANCELED,xtconstant.ORDER_PART_SUCC,xtconstant.ORDER_SUCCEEDED,xtconstant.ORDER_JUNK]
    def settradetool(self,tradertool):
        self.tradertool = tradertool
    def deal_reset_order(self,obj, orderid, o):
        if "resetprice" in o.keys():
            price=o["resetprice"]
            if obj not in self.reset_order_checktick.keys():
                self.reset_order_checktick[obj] = {}
            self.reset_order_checktick[obj][orderid]=price
    def deal_close_all(self,obj,signal,t,pos):
        isdeal=True
        if obj in self.working_orders.keys():
            t=self.working_orders[obj]
            if t:
                # 撤单，撤单成功后委托
                for orderid,o in t.items():
                    self.tradertool.cancel_order_stock(self.account, o.order_id)
                self.orders_watchdog_closeall[obj] = [signal,t,pos]
                isdeal=False
        if isdeal:
            if pos.can_use_volume>0:
                orderid = self.tradertool.order_stock(self.account, obj, xtconstant.STOCK_SELL, pos.can_use_volume,
                                                  xtconstant.FIX_PRICE,
                                                  t["lastPrice"], self.ssname, signal["k"]+signal["msg"])
                now = datetime.datetime.now()
                self.set_watchdog_order(obj, now, orderid)
                print("deal_close_all", self.orders_watchdog)
            else:
                print("deal_close_all", obj, "持仓为0")
    def set_watchdog_order(self,obj,timekey,orderid):
        if obj not in self.orders_watchdog.keys():
            self.orders_watchdog[obj]={}
        self.orders_watchdog[obj][orderid]=[timekey,None,None]
    def ontick(self,obj,timekey,t):
        if obj in self.reset_order_checktick.keys():
            oo=self.reset_order_checktick[obj]
            if oo:
                p=t["lastPrice"]
                deals=[]
                for orderid,price in oo.items():
                    if p<=price:
                        now = datetime.datetime.now()-datetime.timedelta(hours=1)
                        self.set_watchdog_order(obj, now, orderid)
                        deals.append(orderid)
                if deals:
                    for i in deals:
                        del oo[i]
            if not oo:
                del self.reset_order_checktick[obj]
        if obj in self.orders_watchdog.keys():
            o=self.orders_watchdog[obj]
            for orderid,oo in o.items():
                oo[2]=t
                delt=timekey-oo[0]
                delt=delt.total_seconds()
                if delt>=15:
                    self.tradertool.cancel_order_stock(self.account, orderid)
    def deal_ibot_order(self,r):
        # print("ibot",self.qywx_bot_key,self.isibot,r.stock_code,r.traded_volume,r.strategy_name,r.order_type,r.order_status)
        try:
            if self.isibot:
                obj=r.stock_code
                name=obj
                info=self.pricetool0.get_objinfo(obj)
                if info:
                    if "InstrumentName" in info.keys():
                        name=info["InstrumentName"]
                if r.strategy_name == self.ssname:
                    # if r.traded_volume>0:
                    if r.order_type == xtconstant.STOCK_BUY:
                        if r.order_status == xtconstant.ORDER_SUCCEEDED:#xtconstant.ORDER_PART_CANCEL
                            s = name + ':全部成交 \n>**<font color="warning">买入</font>**  \n>股票代码：' + obj + '  \n>成交均价：' + str(
                                r.traded_price) + '  \n>成交数量：' + str(r.traded_volume) + '  \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:",s)
                            notic_qywx.send_markdown(s,self.qywx_bot_key)
                        elif r.order_status == xtconstant.ORDER_PART_CANCEL:
                            s = name + ':部成部撤 \n>**<font color="warning">买入</font>**  \n>股票代码：' + obj + '  \n>成交均价：' + str(
                                r.traded_price) + '  \n>成交数量：' + str(r.traded_volume)+ '  \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s,self.qywx_bot_key)
                        elif r.order_status == xtconstant.ORDER_REPORTED:
                            s = name + ':已报单 \n>**<font color="warning">买入</font>**  \n>股票代码：' + obj +' \n>报单价格：' + str(
                                r.price) +' \n>报单数量：' + str(r.order_volume) +' \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s, self.qywx_bot_key)
                        elif r.order_status == xtconstant.ORDER_JUNK:
                            s = name + ':废单 \n>**<font color="warning">买入</font>**  \n>股票代码：' + obj +' \n>报单价格：' + str(
                                r.price) +' \n>报单数量：' + str(r.order_volume) +' \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s, self.qywx_bot_key)
                    elif r.order_type == xtconstant.STOCK_SELL:
                        if r.order_status == xtconstant.ORDER_SUCCEEDED:
                            s = name + ':全部成交 \n>**<font color="info">卖出</font>**  \n>股票代码：' + obj + '  \n>成交均价：' + str(
                                r.traded_price) + '  \n>成交数量：' + str(r.traded_volume)+ '  \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s,self.qywx_bot_key)
                        elif r.order_status == xtconstant.ORDER_PART_CANCEL:
                            s = name + ':部成部撤 \n>**<font color="info">卖出</font>**  \n>股票代码：' + obj + '  \n>成交均价：' + str(
                                r.traded_price) + '  \n>成交数量：' + str(r.traded_volume)+ '  \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s,self.qywx_bot_key)
                        elif r.order_status == xtconstant.ORDER_REPORTED:
                            s = name + ':已报单 \n>**<font color="info">卖出</font>**  \n>股票代码：' + obj +' \n>报单价格：' + str(
                                r.price) +' \n>报单数量：' + str(r.order_volume) +' \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s, self.qywx_bot_key)
                        elif r.order_status == xtconstant.ORDER_JUNK:
                            s = name + ':废单 \n>**<font color="info">卖出</font>**  \n>股票代码：' + obj +' \n>报单价格：' + str(
                                r.price) +' \n>报单数量：' + str(r.order_volume) +' \n>订单说明：' + str(r.order_remark)
                            print("ibot msg:", s)
                            notic_qywx.send_markdown(s, self.qywx_bot_key)
                    else:
                        print("不是最终状态")
                    # else:
                    #     print("ibot", code2name_order_status[r.order_status])
                else:
                    print("ibot,不是本策略")
            else:
                print("ibot,没有配置机器人")
        except Exception as e:
            print("deal_ibot_order err",e)
    def on_stock_order(self, r):
        """
        委托回报推送
        :param order: XtOrder对象
        :return:
        """

        print("on_stock_order",r.stock_code,r.traded_volume,r.traded_volume)
        try:
            self.ss.on_order(r)
            #self.ss.deal_out_objs(r)
            obj=r.stock_code
            orderid=r.order_id
            stat = r.order_status
            if r.order_volume>r.traded_volume:
                if obj in self.orders_watchdog.keys():
                    if orderid in self.orders_watchdog[obj].keys():
                        self.orders_watchdog[obj][orderid][1]=r
            elif r.order_volume>r.traded_volume:
                if obj in self.orders_watchdog.keys():
                    if orderid in self.orders_watchdog[obj].keys():
                        del self.orders_watchdog[obj][orderid]
                if len(self.orders_watchdog[obj])==0:
                    del self.orders_watchdog[obj]
            if obj not in self.working_orders.keys():
                self.working_orders[obj]={}
            self.working_orders[obj][orderid]=r
            if stat in self.order_end_stat:
                del self.working_orders[obj][orderid]
            if len(self.working_orders[obj])==0:
                if obj in self.orders_watchdog_closeall.keys():
                    signal, t, pos=self.orders_watchdog_closeall[obj]
                    orderid = self.tradertool.order_stock(self.account, obj, xtconstant.STOCK_SELL, pos.can_use_volume,
                                                          xtconstant.FIX_PRICE,
                                                          t["lastPrice"], self.ssname, signal["msg"])
                    now = datetime.datetime.now()
                    self.set_watchdog_order(obj, now, orderid)
                    print("on_stock_order", self.orders_watchdog)
            if r.strategy_name==self.ssname:
                print('本策略-委托回调',"strategy_name",r.strategy_name,r.order_remark,obj,orderid,r.order_type,r.order_status,code2name_order_status[r.order_status],r.status_msg,"order_volume",r.order_volume,r.price,"traded_volume",r.traded_volume,r.price_type)
            else:
                print('外部-委托回调',"strategy_name",r.strategy_name,r.order_remark,obj,orderid,r.order_type,r.order_status,code2name_order_status[r.order_status],"order_volume",r.order_volume,"traded_volume",r.price,r.traded_volume,r.traded_volume,r.price_type)
            self.deal_ibot_order(r)
            # if r.traded_volume > 0 and r.order_status in [xtconstant.ORDER_SUCCEEDED,xtconstant.ORDER_PART_CANCEL]:
            #     if r.strategy_name==self.ssname or r.stock_code in self.ss.out_objs:
            #         self.ss.refresh_pos()
        except Exception as e:
            print("on_stock_order err",e)




    def on_stock_trade(self, r):
        """
        成交变动推送
        :param trade: XtTrade对象
        :return:
        """
        if r.strategy_name==self.ssname:
            print('本策略-成交',"strategy_name",r.strategy_name,r.order_remark,r.stock_code,r.order_id,"order_type",r.order_type,code2name_order_type[r.order_type],datetime.datetime.fromtimestamp(r.traded_time),"traded_price",r.traded_price,"traded_volume",r.traded_volume,"traded_amount",r.traded_amount)

        else:
            print('外部-成交',"strategy_name",r.strategy_name,r.order_remark,r.stock_code,r.order_id,"order_type",r.order_type,code2name_order_type[r.order_type],datetime.datetime.fromtimestamp(r.traded_time),"traded_price",r.traded_price,"traded_volume",r.traded_volume)


    def on_order_error(self, r):
        """
        委托失败推送
        :param order_error:XtOrderError 对象
        :return:
        """
        # print("on order_error callback")
        # print(order_error.order_id, order_error.error_id, order_error.error_msg)
        if r.strategy_name == self.ssname:
            print('本策略-下单错误', "strategy_name", r.strategy_name, r.order_id, r.error_id,r.error_msg)
            s = '下单失败 \n>委托ID：' +str(r.order_id) + '  \n>错误ID：' + str(r.error_id) + '  \n>说明：' + str(r.error_msg)
            print("ibot msg:", s)
            notic_qywx.send_markdown(s, self.qywx_bot_key)

    def on_cancel_error(self, r):
        """
        撤单失败推送
        :param cancel_error: XtCancelError 对象
        :return:
        """
        if r.strategy_name == self.ssname:
            print('本策略-撤单错误', "strategy_name", r.strategy_name, r.order_id, r.error_id, r.error_msg)
            s = '撤单失败 \n>委托ID：' +str(r.order_id) + '  \n>错误ID：' + str(r.error_id) + '  \n>说明：' + str(r.error_msg)
            print("ibot msg:", s)
            notic_qywx.send_markdown(s, self.qywx_bot_key)

    def on_order_stock_async_response(self, r):
        """
        异步下单回报推送
        :param r: XtOrderResponse 对象
        :return:
        """
        if r.strategy_name == self.ssname:
            print(f"异步委托回调 {r.order_remark}")

    def on_cancel_order_stock_async_response(self, r):
        """
        :param r: XtCancelOrderResponse 对象
        :return:
        """
        if r.strategy_name == self.ssname:
            print("异步撤单回调", sys._getframe().f_code.co_name)


    def on_account_status(self, r):
        """
        :param r: XtAccountStatus 对象
        :return:
        """
        print("账号状态",r.account_id,r.broker_type,r.platform_id,r.account_classification,r.login_status)
class tradetool():
    def __init__(self,config):
        self.fund={}
        self.pos={}
        self.orders=[]
        self.working_orders={}
        self.trades=[]

        self.connect_result=-1
        if "qmt_path" in config.keys():
            session_id = int(time.time())
            self.tradertool = XtQuantTrader(config["qmt_path"], session_id)
            # 创建资金账号为 800068 的证券账号对象
            self.account = StockAccount(config["account_id"], config["account_type"])
            # 创建交易回调类对象，并声明接收回调
            self.ontradertool = onTraderTool(self.tradertool,self.account,self,config)
            self.tradertool.register_callback(self.ontradertool)
            # 启动交易线程
            self.tradertool.start()
            # 建立交易连接，返回0表示连接成功
            connect_result = self.tradertool.connect()
            if connect_result!=0:
                print("交易连接失败，错误代码为",connect_result)
                print("请检查后再启动策略")
                # sys.exit()
            print('建立交易连接，连接成功', connect_result)
            subscribe_result = self.tradertool.subscribe(self.account)
            print('对交易回调进行订阅，订阅后可以收到交易主推，返回0表示订阅成功', subscribe_result)
            self.connect_result=connect_result
        # return self.connect_result
    def login(self,qmt_path=None,account_id=None,account_type=None):
        if qmt_path is not None and account_id is not None and account_type is not None:
            session_id = int(time.time())
            self.tradertool = XtQuantTrader(qmt_path, session_id)
            # 创建资金账号为 800068 的证券账号对象
            self.account = StockAccount(account_id, account_type)
            # 创建交易回调类对象，并声明接收回调
            self.ontradertool = onTraderTool(self.tradertool, self.account, self, self.config)
            self.tradertool.register_callback(self.ontradertool)
            # 启动交易线程
            self.tradertool.start()
            # 建立交易连接，返回0表示连接成功
            connect_result = self.tradertool.connect()
            if connect_result != 0:
                print("交易连接失败，错误代码为", connect_result)
                print("请检查后再启动策略")
                # sys.exit()
            print('建立交易连接，连接成功', connect_result)
            subscribe_result = self.tradertool.subscribe(self.account)
            print('对交易回调进行订阅，订阅后可以收到交易主推，返回0表示订阅成功', subscribe_result)
            self.connect_result = connect_result
        self.qry_info()
        return self.connect_result
    def logout(self):
        self.tradertool.stop()
        return True
    def cancel_order(self,orderid):
        self.tradertool.cancel_order_stock(self.account, orderid)
    def qry_info(self,req={}):
        self.get_fund()
        self.get_pos()
        self.get_orders()
        self.get_trade()
    def get_orders(self,isworking=False):
        """
        stock_code	str	证券代码，例如"600000.SH"
        order_id	int	订单编号
        order_sysid	str	柜台合同编号
        order_time	int	报单时间
        order_type	int	委托类型，参见数据字典
        order_volume	int	委托数量
        price_type	int	报价类型，该字段在返回时为柜台返回类型，不等价于下单传入的price_type，枚举值不一样功能一样，参见数据字典
        price	float	委托价格
        traded_volume	int	成交数量
        traded_price	float	成交均价
        order_status	int	委托状态，参见数据字典
        status_msg	str	委托状态描述，如废单原因
        strategy_name	str	策略名称
        order_remark	str	委托备注，最大 24 个英文字符
        :param isworking:
        :return:
        """
        self.working_orders = {}
        oo=[]
        os=self.tradertool.query_stock_orders(self.account, cancelable_only = isworking)

        if os:
            for c in os:
                obj, mkcode = tool.xtcode2obj(c.stock_code)
                openclose,bs,orderinfo=tool.xt2openclose(c.order_type,c.price_type)
                order = {"obj": obj, "code": c.stock_code, "mkcode": mkcode, "bs": bs,"openclose":openclose,
                         "orderid": c.order_id, "sysid": c.order_id,"qty": c.order_volume,"qty1":c.traded_volume,
                        "price": c.price,"price1":c.traded_price, "status": tool.xtorder_status2order_status(c.order_status), " statusmsg": code2name_order_status[c.order_status]+":"+str(c.order_status)+c.status_msg,
                        "ssid": c.strategy_name, "orderinfo": c.order_remark+","+orderinfo}
                order["qty0"]=c.order_volume-c.traded_volume
                order["timekey"]=datetime.datetime.fromtimestamp(c.order_time)
                for k, v in tool.xt2dict(c).items():
                    if k not in order.keys():
                        order[k] = v
                oo.append(order)
                if order["qty0"]>0:
                    self.working_orders[order["orderid"]]=order
                    print("working_orders", order)
                else:
                    print("orders", order)
        self.orders=oo
        return oo
    def get_pos(self):
        """
        stock_code	str	证券代码
        volume	int	持仓数量
        can_use_volume	int	可用数量
        open_price	float	开仓价
        market_value	float	市值
        frozen_volume	int	冻结数量
        on_road_volume	int	在途股份
        yesterday_volume	int	昨夜拥股
        avg_price	float	成本价
        direction	int	多空方向，股票不适用；参见数据字典

        :return:
        """
        position = self.tradertool.query_stock_positions(self.account)
        poss={}
        if position:
            for c in position:
                if c.volume!=0:
                    obj,mkcode=tool.xtcode2obj(c.stock_code)
                    pos = {"obj": obj, "code": c.stock_code,"mkcode":mkcode,"bs":"buy",
                             "qty": c.volume, "qty_avl": c.can_use_volume,"qty_yesterday":c.yesterday_volume,"qty_today":c.volume-c.can_use_volume,
                             "price": c.avg_price, "margin": c.market_value}
                    for k, v in tool.xt2dict(c).items():
                        if k not in pos.keys():
                            pos[k] = v
                    if obj not in poss.keys():
                        poss[obj]={}
                    poss[obj]["buy"]=pos
                    print("pos", pos)
        self.pos=poss
        return poss

    def get_fund(self):
        """
        cash	float	可用金额
        frozen_cash	float	冻结金额
        market_value	float	持仓市值
        total_asset	float	总资产
        :return:
        """
        c= self.tradertool.query_stock_asset(self.account)

        fund={"cash":c.cash,"frozen0":c.frozen_cash,"margin":c.market_value,"balance":c.total_asset}
        for k,v in tool.xt2dict(c).items():
            if k not in fund.keys():
                fund[k]=v
        self.fund=fund
        print("fund",fund)
        return fund
    def get_trade(self):
        t=self.tradertool.query_stock_trades(self.account)
        trades=[]
        if t:
            for c in t:
                tt={"timekey":datetime.datetime.fromtimestamp(c.traded_time)}
                for k, v in tool.xt2dict(c).items():
                    tt[k]=v
                trades.append(tt)
                print("trade", tt)
        self.trades=trades
        return trades
    def openbuy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos openbuy",timekey,qty,posrate,amount,price,msg,ssid,obj,code)
        if code is None:
            code=tool.obj2xtcode(obj)
        if price is None:
            price_type=xtconstant.LATEST_PRICE
        else:
            price_type=xtconstant.FIX_PRICE
        r=self.tradertool.order_stock(self.account, stock_code=code, order_type=xtconstant.STOCK_BUY, order_volume=qty, price_type=price_type, price=price, strategy_name=ssid, order_remark=msg)
        if r==-1:
            print("openbuy失败")
        else:
            print("openbuy成功,订单编号",r)
        return r
    def closesell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        print("pos closesell", timekey, qty, posrate, amount, price, msg, ssid, obj, code)
        if code is None:
            code=tool.obj2xtcode(obj)
        if price is None:
            price_type=xtconstant.LATEST_PRICE
        else:
            price_type=xtconstant.FIX_PRICE
        r=self.tradertool.order_stock(self.account, stock_code=code, order_type=xtconstant.STOCK_SELL, order_volume=qty, price_type=price_type, price=price, strategy_name=ssid, order_remark=msg)
        if r==-1:
            print("closesell失败")
        else:
            print("closesell成功,订单编号",r)
        return r
    def buy(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        return self.openbuy(timekey, qty, posrate, amount, price, msg, ssid, obj, code)
    def sell(self,timekey,qty,posrate=None,amount=None,price=None,msg="",ssid="",obj=None,code=None):
        return self.closesell(timekey,qty,posrate,amount,price,msg,ssid,obj,code)
    def on_order(self,r):
        pass
    def on_trade(self,r):
        pass
    def on_account_status(self,r):
        pass

class tradetool_credit(tradetool):
    def get_pos(self):
        position = self.tradertool.query_stock_positions(self.account)
        poss={}
        if position:
            for i in position:
                if i.volume!=0:
                    t = {}
                    t["account_type"] = i.account_type
                    t["account_id"] = i.account_id
                    t["stock_code"] = i.stock_code
                    t["volume"] = i.volume
                    t["can_use_volume"] = i.can_use_volume
                    t["open_price"] = i.open_price
                    t["market_value"] = i.market_value
                    t["obj"] = i.stock_code
                    print("获取当日持仓",t)
                    obj=i.stock_code
                    poss[obj]=t
        return poss

    def get_fund(self):
        c= self.tradertool.query_stock_asset(self.account)



if  __name__=="__main__":
    import sys
    from jili.core.config import load_config_json

    c = load_config_json(r"E:\qjs\zjhhzc\stoppl\ss\multifactor\pos_calc_live\config.json")
    print("config.json:", c)
    t0=tradetool(c)